# 开始一个vue3的项目

## 1.element plus的使用

#### 1.1安装依赖

```
pnpm install element-plus
```

```
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

const app = createApp(App)

app.use(ElementPlus)
app.mount('#app')
```

#### 1.2Element Plus 提供了全局配置国际化的配置。

main.ts

```
import ElementPlus from 'element-plus'

//@ts-ignore    忽略 TypeScript 错误
import zhCn from 'element-plus/es/locale/lang/zh-cn'

app.use(ElementPlus, {
  locale: zhCn,
})
```

## 2.src别名的配置

### 2.1准备工作

在开发项目的时候文件与文件关系可能很复杂，因此我们需要给src文件夹配置一个别名！！！

[安装](https://so.csdn.net/so/search?q=安装&spm=1001.2101.3001.7020) @types/node 避免[代码爆红](https://so.csdn.net/so/search?q=代码爆红&spm=1001.2101.3001.7020)

```
pnpm install @types/node
```

### 2.2根目录下 vite.config.ts 文件中配置

```
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';

// https://vite.dev/config/
export default defineConfig({
  plugins: [vue()],

  resolve: {
    // 设置文件./src路径为 @
     alias: [
      {
        find: '@',
        replacement: path.resolve(__dirname, './src')
      }
    ]
  },
  
  server: {
    host: 'localhost',
    port: 3000,
    open: true
  }
})


```

### 2.3、根目录下 tsconfig.json 文件中配置

```
// tsconfig.json
{
  "compilerOptions": {
   		
    "baseUrl": "./",	// 解析非相对模块的基地址，默认是当前目录
    "paths": {
      "@/*": ["src/*"]	 //将 @/xxx 映射为 src/xxx，方便使用绝对路径
    }
  }
}
```

![image-20250510195159377](C:\Users\Administrator\Desktop\桌面文件备份\笔记\vue\md文档\开始vue3的项目\image-20250510195159377.png)

## 3. 环境变量的配置

开发环境：为开发人员提供一个安全的地方来进行编码和调试，不会影响到其他环境，一般来说开发人员在本地机器上运行和测试应用程序。
测试环境：用于测试，模拟生产环境，确保新功能在部署前能够正常工作，并且不会影响现有功能。
生产环境：是面向用户的最终环境，任何更改都必须经过严格的测试才能部署到这里，通常具有优化和最少的日志记录。

**node 通过 `vue-cli-service`工具（也称之为脚手架）将前端中使用`process.env`的地方，在`build`（构建或打包）时，替换为node环境中的`process.env`的值**。

##### **Vite 环境变量加载规则**

1. **基础文件优先**
   无论当前模式如何，`.env` 文件总是会被加载（通用配置）。
2. **模式文件覆盖**
   当运行 `vite` 或 `vite dev` 时：
   - 自动匹配 `.env.development`（开发模式专用）
   - **同名变量会覆盖 `.env` 中的值**
3. **意外情况触发**
   如果出现“显示 development 却读取 `.env`”的现象，可能是以下原因：
   - 📌 **变量名未以 `VITE_` 开头**
     Vite ​**​只暴露​**​以 `VITE_` 开头的变量（需检查 `.env.development` 的变量命名）。
   - 📌 **未正确启动开发模式**
     检查是否误用了 `vite build` 或其他命令（开发模式需用 `vite dev`）。
   - 📌 **存在更高优先级的文件**
     如 `.env.local` 或 `.env.development.local` 可能覆盖配置（Vite 优先级规则见下文）

##### .env.development



```
# 环境变量
NODE_ENV='development'
# API URL
VITE_APP_BASE_API='http://localhost:3333'
# 标题
VITE_APP_TITLE='Vue3 Admin'
#
VITE_SERVE='http://localhost:3333'

```

##### .env.test

```
# 环境变量
NODE_ENV='test'
# API URL
VITE_APP_BASE_API='test/api'
# 标题
VITE_APP_TITLE='Vue3 Admin'
#
VITE_SERVE='http://localhost:3333'

```

##### .env.production

```
NODE_ENV='production'
# API URL
VITE_APP_BASE_API='http://localhost:3333'
# 标题
VITE_APP_TITLE='Vue3 Admin'
#
VITE_SERVE='http://localhost:3333'
```



在package.json中定义运行项目的脚本命令

```
  "scripts": {
  "build:test": "vue-tsc  && vite build --mode test",
  "build:pro": "vue-tsc  && vite build --mode pro",
  }
```

可以使用import.meta.env获取文件里的值

console.log(import.meta.env)

![image-20250510203849415](C:\Users\Administrator\Desktop\桌面文件备份\笔记\vue\md文档\开始vue3的项目\image-20250510203849415.png)



console.log(process.env.NODE_ENV);    输出一个develoment

console.log(import.meta.env)   得到一个对象

![image-20250511132857220](C:\Users\Administrator\Desktop\桌面文件备份\笔记\vue\md文档\开始vue3的项目\image-20250511132857220.png)

## 4.集成svg图标

#### 安装SVG依赖

```
pnpm install vite-plugin-svg-icons D
```

#### 配置文件

在`vite.config.js`中配置`vite-plugin-svg-icons`

```
// 其他引入
import path from 'path'
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
export default defineConfig({
// 其他配置
  plugins: [
    vue(),
    createSvgIconsPlugin({
      iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
      symbolId: 'icon-[dir]-[name]',
    }),
  ],
});

```



- 在`main.js`中引入注册脚本

```
import "virtual:svg-icons-register";

```

- 至此依赖部分已经完成。
- 将svg文件按照类别保存在对应的`iconSvgs`或`fileSvgs`文件夹中，根据你的命名即可。

使用方式

```
 	<svg style="height: 60px; width:60px">
 	
      <use xlink:href="#icon-notice" fill="red"></use>
      
    </svg>

```

封装自己的svg组件

```
<template>
  <svg :style="{ width, height }">
    <use :href="prefix + name" :fill="color"></use>
  </svg>
</template>

<script setup lang="ts">
// 接收父组件传递过来的参数
defineProps({
  prefix: {
    type: String,
    default: '#icon-'
  },
  // svg名称
  name: String,
  // svg颜色
  color: {
    type: String,
    default: '#ffffff'
  },
  // svg宽度
  width: {
    type: String,
    default: '16px'
  },
  // svg高度
  height: {
    type: String,
    default: '16px'
  }
})
</script>

```

- 至此svg组件已经完成，在其他页面导入即可使用，注意`name`属性需要和保存的svg文件名一致。

```
<template>
  <div>
   // 注意这里的name需要和svg的文件名保持一致，驼峰命名SvgIcon和svg-icon是一样的
    <SvgIcon name="lock" color="#a8abb2" width="20px" height="20px"></SvgIcon>
  </div>
</template>

<script setup>
  import SvgIcon from "@/components/SvgIcon/index.vue";
</script>

```

####  全局组件引入SvgIcon

- 为了方便开发，建议将一些常用组件一并注册至app全局，在任意页面直接使用不需要引用文件。
- 在`src/components`路径下创建`install.js`文件

```
// 引入项目中的全局组件
import SvgIcon from "./SvgIcon/index.vue"; 
// 其他需要引入组件

 // 将自定义组件放入对象中
const allGloablCom = {
  SvgIcon,
// 其他需要引入组件
};

// 对外暴露插件对象
export default {
  // 只能叫做 install 方法
  install(app) {
    // 循环遍历，注册 allGloablCom 中全部组件
    Object.keys(allGloablCom).forEach((key) => {
      // 注册为全局组件
      app.component(key, allGloablCom[key]);
    });
  }
};


```

####  一些建议

1. 第三方下载 svg，需设置 `fill`属性为`#000000`或`currentColor`或者直接删掉 fill 属性，否则颜色无法通过传参修改。

2. 推荐几个svg下载地址：

   - [阿里官方图标库](https://www.iconfont.cn/collections/detail?spm=a313x.collections_index.i1.d9df05512.5dda3a81f6X9yQ&cid=19238&page=1)
   - [ant design 图标库](https://www.iconfont.cn/collections/detail?spm=a313x.collections_index.i1.d9df05512.5dda3a81f6X9yQ&cid=9402)
   - 实际项目 [ui 设计](https://so.csdn.net/so/search?q=ui 设计&spm=1001.2101.3001.7020)稿中下载 svg 图片。

   

一些隐患
缺少了element icon的加持，一些带有icon的组件会出现一些bug，例如在plain模式下的带icon的按钮，SvgIcon无法正确的根据鼠标移入移出进行反色：
// 在plain模式下的带icon的按钮，svgicon无法正确的根据鼠标移入移出反色

```
// 在plain模式下的带icon的按钮，svgicon无法正确的根据鼠标移入移出反色
<el-button type="primary" plain>
	Upload
	<SvgIcon name="upload"/>
</el-button>

目前为止，我只发现了这一个bug，处理办法请参考这篇文档：【VUE3.0】解决element button在plain模式下icon无法反色问题
如果在封装svg组件后遇到其他与之相关的bug，欢迎交流探讨如何解决。

```

#### 引入全局组件

在入口文件暴露一个文件src/index.ts文件,通过app.use方法安装自定义插件到全局中

```
import gloablComponent from './components/index';
app.use(gloablComponent);
```

在src文件夹目录下创建一个index.ts文件：

用于注册components文件夹内部全部全局组件！！！

```
//引入项目中全部的全局组件
import SvgIcon from './SvgIcon/index.vue';
//全局对象
import type { App, Component } from 'vue';
//对外暴露插件对象
const components: { [name: string]: Component } = { SvgIcon };
export default {
//务必叫做install方法
    install(app: App) {
    //获取对象里所有的ksy，遍历key
        Object.keys(components).forEach((key: string) => {
        	//通过key获取vlues,注册到全局中去
            app.component(key, components[key]);
        })
    }
}
```

## 5.集成scss

我们目前在组件内部已经可以使用scss样式,因为在配置styleLint工具的时候，项目当中已经安装过sass sass-loader,因此我们再组件内可以使用scss语法！！！需要加上lang="scss"



如果需要重新安装

```
pnpm add sass 
sass-loader stylelint postcss postcss-scss postcss-html stylelint-config-prettier stylelint-config-recess-order stylelint-config-recommended-scss stylelint-config-standard stylelint-config-standard-vue stylelint-scss stylelint-order stylelint-config-standard-scss -D

```

还缺一个插件

```
pnpm add -D sass-embedded
```

需要加上lang="scss"

```
<style scoped lang="scss"></style>
```

在入口文件中引入 scss全局样式文件

```
//  引入全局样式
import '@/styles/index.scss'
```

从npm 中搜索reset.scss 粘贴过来，在src目录下新建styles/reset.scss

把代码粘贴到 自己的reset.scss中



新建styles/index.scss,把样式导入index.scss中

```
@import "./reset.scss"
如果@import无效，换成  

@use "./reset”         @use不需要.scss扩展名。
```

这是全局样式已经生效了。



但是你会发现在src/styles/index.scss全局样式文件中没有办法使用$变量.因此需要给项目中引入全局变量$.

在style/variable.scss创建一个variable.scss文件！

```
//variable.scss
//项目的全局样式
//定义项目的主题色

$base-color: yellow;
// src/styles/variable.scss
$primary-color: #42b883;
```

在vite.config.ts文件配置如下:

```
//vite.config.ts

  // scss全局变量
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@use "@/styles/variable.scss" as var;', // 使用 @use 替代 @import
      },
    },
  },
```

配置完毕你会发现scss提供这些全局变量可以在组件样式中使用了！！

```
<style scoped lang="scss">
  h1{
    color:var.$primary-color;
  }
</style>

```

## 6.mock数据

安装依赖: https://www.npmjs.com/package/vite-plugin-mock

```
pnpm install -D vite-plugin-mock mockjs
```

在 vite.config.js 配置文件启用插件。

```
// vite.config.ts
import { defineConfig } from 'vite'
import { viteMockServe } from 'vite-plugin-mock'


export default defineConfig(({ command }) => {
  return {
    plugins: [
      vue(),
      //引入mock
      viteMockServe({
        mockPath: 'src/mock', // mock文件路径
        enable: process.env.NODE_ENV === 'development', // 开发环境才启用
        logger: true // 是否在控制台打印mock信息
      }),

      // svg图标
      createSvgIconsPlugin({
        //指定要缓存的图标文件夹
        iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
        // symbolId格式
        symbolId: 'icon-[dir]-[name]'
      }),

      
     
    ],
```

在根目录创建mock文件夹:去创建我们需要mock数据与接口！！！

在mock文件夹内部创建一个user.ts文件

```
//用户信息数据
function createUserList() {
    return [
        {
            userId: 1,
            avatar:
                'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
            username: 'admin',
            password: '111111',
            desc: '平台管理员',
            roles: ['平台管理员'],
            buttons: ['cuser.detail'],
            routes: ['home'],
            token: 'Admin Token',
        },
        {
            userId: 2,
            avatar:
                'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
            username: 'system',
            password: '111111',
            desc: '系统管理员',
            roles: ['系统管理员'],
            buttons: ['cuser.detail', 'cuser.user'],
            routes: ['home'],
            token: 'System Token',
        },
    ]
}
 
export default [
    // 用户登录接口
    {
        url: '/api/user/login',//请求地址
        method: 'post',//请求方式
        response: ({ body }) => {
            //获取请求体携带过来的用户名与密码
            const { username, password } = body;
            //调用获取用户信息函数,用于判断是否有此用户
            const checkUser = createUserList().find(
                (item) => item.username === username && item.password === password,
            )
            //没有用户返回失败信息
            if (!checkUser) {
                return { code: 201, data: { message: '账号或者密码不正确' } }
            }
            //如果有返回成功信息
            const { token } = checkUser
            return { code: 200, data: { token } }
        },
    },
    // 获取用户信息
    {
        url: '/api/user/info',
        method: 'get',
        response: (request) => {
            //获取请求头携带token
            const token = request.headers.token;
            //查看用户信息是否包含有次token用户
            const checkUser = createUserList().find((item) => item.token === token)
            //没有返回失败的信息
            if (!checkUser) {
                return { code: 201, data: { message: '获取用户信息失败' } }
            }
            //如果有返回成功信息
            return { code: 200, data: {checkUser} }
        },
    },
]
```

在入口文件main.ts

```
//测试mock
import axios from 'axios'
axios.post('/api/user/login',{username:'admin',password:'111111'}).then(res => {
    console.log(res.data)
})

```



## 7.axios二次封装

在开发项目的时候避免不了与后端进行交互,因此我们需要使用axios插件实现发送网络请求。在开发项目的时候我们经常会把axios进行二次封装。
目的:
1:便用请求拦截器，可以在请求拦截器中处理一些业务(开始进度条、请求头携带公共参数)2:使用响应拦截器，可以在响应拦截器中处理一些业务(进度条结束、简化服务器返回的数据、处理htp网络错误)



### 7.1安装插件

```
pnpm install axios
```





###  7.2src中创建request.ts

```
import axios from 'axios'
import { ElMessage } from 'element-plus'
//创建axios实例
let request = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: 5000
})
//请求拦截器
request.interceptors.request.use((config) => {
  return config
})
//响应拦截器
request.interceptors.response.use(
  (response) => {
    return response.data
  },
  (error) => {
    //处理网络错误
    let msg = ''
    let status = error.response.status
    switch (status) {
      case 401:
        msg = 'token过期'
        break
      case 403:
        msg = '无权访问'
        break
      case 404:
        msg = '请求地址错误'
        break
      case 500:
        msg = '服务器出现问题'
        break
      default:
        msg = '无网络'
    }
    ElMessage({
      type: 'error',
      message: msg
    })
    return Promise.reject(error)
  }
)
export default request

```

 ### 7.3请求拦截器的常见使用场景

```
tsrequest.interceptors.request.use((config) => {
  // 在发送请求之前做些什么
  return config; // 必须返回 config
}, (error) => {
  // 处理请求错误
  return Promise.reject(error);
});
```

------

####  7.3.1常见用法示例

##### 1. **添加 Token 到请求头**

**（如 JWT 认证##）**

```
tsrequest.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  return config;
});
```

##### 2. **统一设置 Content-Type**

**（如 JSON 或 form-data）**

```
tsrequest.interceptors.request.use(config => {
  config.headers['Content-Type'] = 'application/json;charset=utf-8';
  return config;
});
```

##### 3. **为某些接口添加特定参数**

**（如时间戳防缓存）**

```
tsrequest.interceptors.request.use(config => {
  // 只针对 get 请求添加时间戳
  if (config.method === 'get') {
    config.params = { ...config.params, _t: new Date().getTime() };
  }
  return config;
});
```

##### 4. **请求时显示 loading 动画**

**或日志记录**

```
tsrequest.interceptors.request.use(config => {
  console.log('发起请求:', config.url);
  // 显示全局 loading（需配合 UI 框架）
  // store.dispatch('showLoading');
  return config;
});
```

| 要点                                    | 说明                               |
| :-------------------------------------- | :--------------------------------- |
| ⚠️ 必须返回 [config](javascript:void(0)) | 否则请求无法继续发出               |
| ❗ 错误处理函数可选                      | 如果不处理错误，可以省略第二个参数 |
| 📦 可多次调用 `.use()`                   | 多个拦截器会按添加顺序依次执行     |

------

 示例：完整带 Token 和日志的拦截器

```
tsrequest.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }

  console.log('请求地址:', config.url);
  return config;
});
```

## 8. api统一管理接口

在scr下创建api文件夹

依次创建 user、 product、 acl   ......等文件夹

在user文件下创建index.ts,用于统一管理用户相关的接口

在user文件下创建type.ts用于统一管理用户相关的    接口返回的数据类型，接口需要的携带的参数类型

用户的登录接口示例：

在index.ts中

```
//统一管理用户相关得到api
import request from '@/utils/request'
import type { LoginFrom } from './type'
//使用枚举统一url,大写api常量大写
enum API {
  LOGIN_URL = '/user/login',
  USERINFO_URL = '/userInfo',
}

//暴露请求函数
export const reqLogin = (data: LoginFrom) => request.post(API.LOGIN_URL, data)
export const reqUserInfo = () => request.get(API.USERINFO_URL)
```

创建api/user/type.ts

```
//登录接口需要的携带的参数类型
export interface LoginFrom {
  username: string
  password: string
}

//token类型接口
interface dataType {
  token: string
}

//登录接口返回的数据类型
export interface LoginResponseData {
  code: number
  data: dataType
}
//用户信息接口返回的数据类型
interface UserInfo {
  checkUser: {
    userId: number
    avatar: string
    username: string
    password: string
    desc: string
    roles: string[]
    buttons: string[]
    routes: string[]
    token: string
  }
}

//定义用户信息接口返回的数据类型
export interface UserInfoResponseData {
  code: number
  data: UserInfo
}

```

测试发现可以

```
//测试api
<script setup lang="ts">
import { onMounted } from 'vue';
import { reqLogin, reqUserInfo } from '@/api/user';
onMounted(() => {
    reqLogin({ username: 'admin', password: '111111' })
})
</script>
```

## 9.路由的配置

安装依赖

```
pnpm install vue-router
```



在src文件中创建router/index.ts 、 router/routes.ts文件



router/index.ts 

```
//导入路由依赖
import { createRouter, createWebHashHistory } from 'vue-router'
//  引入路由常量路由数组
import { constantRoutes } from './routes'

let router = createRouter({
  history: createWebHashHistory(), // hash模式
  routes: constantRoutes, // 路由数组
  scrollBehavior() {
    // 滚动行为
    return { top: 0, left: 0 }
  },
})

export default router

```

 router/routes.ts



```
//配置常量路由暴露出去
export const constantRoutes = [
  {
    //登录成功展示数据的路由
    path: '/',
    redirect: '/home',
  },
  {
    //首页
    path: '/home',
    component: () => import("@/views/home/index.vue"),
    name: 'home', //将来做菜单权限
  },
  {
    //登录路由
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    name: 'login',
  },
  {
    //404
    path: '/404',
    component: () => import('@/views/404/index.vue'),
    name: '404',
  },
  {
    //任意路由
    path: '/:pathMatch(.*)*',
    redirect: '/404',
    name: 'any',
  },
]

```

入口文件引入

```
//  引入路由
import router from '@/router'
app.use(router)
```

##  10. pinia仓库

下载依赖

```
pnpm install pinia
```

在src下创建store/index.ts

```
import { createPinia } from "pinia";

//创建仓库
const pinia = createPinia();
export default pinia;
```

在入口文件

```
//引入仓库
import pinia from './store'
app.use(pinia);
```

创建src/store/modules/user.ts

```
//用户数据模块

import { defineStore } from 'pinia';
//创建用户小仓库
let useUserStore = defineStore("user",{
	//存储数据的地方
    state:() => {
        return {
        
        }
    },
    //处理异步|逻辑的地方
    actions:{
        
    },
    getters:{

    }
})

//用户模块仓库中可以引入请求函数、路由信息等，其它模块如此配置即可
export default useUserStore;
```

在模块中使用

```
//引入用户的小仓库
import { useUserStore } from '@/store/modules/user'
const userStore = useUserStore()

调用小仓库中的方法
userStore.userLogin(user.value)
```

## 11. 自定义表单校验

### el-form 组件属性

1. **ref="formRef"**
   - 用于获取表单组件实例，可以通过它调用表单的各种方法
   - 在`<script setup>`中使用`const formRef = ref(null)`定义
2. **:model="form"**
   - 绑定表单数据对象，必须是一个响应式对象（如使用`reactive`创建）
   - 表单中的每个字段都应该在这个对象中定义
3. **:rules="rules"**
   - 绑定表单验证规则对象
   - 规则对象的键名必须与表单字段名一致
4. **label-width**
   - 设置表单标签的宽度
   - 可以是固定值如`120px`，也可以是响应式变量

### el-form-item 组件属性

1. **prop**
   - 对应表单数据中的字段名
   - 必须与`rules`对象中的键名一致，否则验证不会生效
2. **label**
   - 表单项的标签文本

### 验证规则对象

验证规则对象中的每个字段可以包含多个规则，每个规则可以是：

1. **required**
   - 是否必填字段
   - `{ required: true, message: '请输入内容', trigger: 'blur' }`
2. **min/max**
   - 最小/最大长度限制
   - `{ min: 3, max: 10, message: '长度在3到10个字符', trigger: 'blur' }`
3. **validator**
   - 自定义验证函数
   - 函数接收三个参数：`(rule, value, callback)`
   - 必须调用`callback()`表示验证通过，或`callback(new Error('错误信息'))`表示验证失败
4. **trigger**
   - 触发验证的时机
   - `blur`：失去焦点时验证
   - `change`：值变化时验证

### 表单方法

1. **validate(callback)**
   - 对整个表单进行验证
   - 接收一个回调函数，参数`valid`表示验证是否通过
2. **validateField(prop, callback)**
   - 验证特定字段
   - `prop`：要验证的字段名
   - `callback`：验证完成后的回调函数
3. **resetFields()**
   - 重置表单，将所有字段值重置为初始值
   - 清除所有验证状态和错误信息
4. **clearValidate(props)**
   - 清除特定字段或所有字段的验证状态
   - 不传参数则清除所有验证状态
   - 可以传入字段名数组清除指定字段的验证状态



添加这些属性

获取表单中的数据 :model="user"绑定表单数据对象 

```
 <el-form class="login-form" :rules="rules" :model="user" status-icon ref="loginForms">
```

在el-form上添加prop

```
<el-form-item prop="username" >
```

:rules="rules"

```
//表单校验的rules
  const rules = {
    //校验属性意思：
    //required： 是否必填
    //min： 最小长度
    //max： 最大长度
    //pattern： 正则校验
    //trigger： 触发校验方式，默认是blur 
    //message： 错误提示信息

    username: [
      {
        required: true,
        min:3,
        max: 12,
        message: '用户名长度在6-12位之间',
        trigger: 'change',
      },
    ],
    password: [
      {
        required: true,
        min:3,
        max: 12,
        message: '用户名长度在6-12位之间',
        trigger: 'change',
      },
    ],
  }
```

ref="loginForms"

保证全部表单相校验通过再发请求

```
const loginForms = ref(null)

在点击事件方法的最上面添加下面这句话可以保证全部表单相校验通过再发请求
await loginForms.value.validate()
```

